﻿#include "WebSocketProtocol.h"

#include <string.h>
#include <assert.h>

#if defined(WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <Winsock2.h>
#include <WS2tcpip.h>
#include <ws2bth.h>
#endif

#if defined(LINUX)
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#include <QString>
#include <qdebug.h>

enum WebSocketStreamSize {
        SmallStream = 125,
        MediumStream = 126,
        BigStream = 127
    };

WebSocketProtocol::WebSocketProtocol()
{
	init();
}

void WebSocketProtocol::encode(char *data, unsigned int &len)
{
	int pos = 0;
	memcpy(_payload_data, data, len);
	memset(data, 0, MAXLINE);
	memset(data, 0x81, 1);
	++pos;
    if (len <= WebSocketStreamSize::SmallStream) {
		memset(data + pos, len, 1);
		++pos;
	}
	else {
		memset(data + pos, 126, 1);
		++pos;
		uint16_t length = htons(len);
		memcpy(data + pos, &length, 2);
		pos += 2;
	}
	memcpy(data + pos, _payload_data, len);
	len = pos + len;
}

void WebSocketProtocol::decode(const char *msg, unsigned int len)
{
	int pos = 0;
	_fin = (unsigned char)msg[pos] >> 7;
	_opcode = msg[pos] & 0x0f;
	++pos;
	_mask = (unsigned char)msg[pos] >> 7;
	_payload_length = msg[pos] & 0x7f;
	++pos;

    //0-125 ：则Payload data的长度即为该值
    //126 ：那么接下来的2个字节才是Payload Data的长度（unsigned）
    //127 ：那么接下来的8个字节才是Payload Tada的长度（unsigned）
    //
    /*
     * PayloadData的长度：7位，7+16位，7+64位
        如果其值在0-125，则是payload的真实长度。
        如果值是126，则后面2个字节形成的16位无符号整型数的值是payload的真实长度。
        注意，网络字节序，需要转换。
        如果值是127，则后面8个字节形成的64位无符号整型数的值是payload的真实长度。
        注意，网络字节序，需要转换。
        长度表示遵循一个原则，用最少的字节表示长度（我理解是尽量减少不必要的传输）。
        举例说，payload真实长度是124，在0-125之间，必须用前7位表示；不允许长度1是126或127，
        然后长度2是124，这样违反原则。
    */

    if (_payload_length == WebSocketStreamSize::MediumStream) {
		uint16_t length = 0;
		memcpy(&length, msg + pos, 2);
		pos += 2;
		_payload_length = ntohs(length);
	}
    else if (_payload_length == WebSocketStreamSize::BigStream) {
		uint32_t length = 0;
        memcpy(&length, msg + pos, 8);
        pos += 8;
		_payload_length = ntohl(length);
	}



    if (_mask == 1)
    {
		for (int i = 0; i < 4; ++i)
			_masking_key[i] = msg[pos + i];
		pos += 4;
	}

	if (_mask != 1){
		memcpy(_payload_data, msg + pos, _payload_length);
	}
    else
    {
        for (uint32_t i = 0; i < _payload_length; ++i)
        {
			int j = i % 4;
			_payload_data[i] = msg[pos + i] ^ _masking_key[j];
		}
	}

    //QString m_payload_data(_payload_data);

    //qDebug() <<"-----recv data:"<< m_payload_data.toLocal8Bit();
}

const char *WebSocketProtocol::payload_data()
{
	return _payload_data;
}

uint64_t WebSocketProtocol::payload_length() const
{
	return _payload_length;
}

void WebSocketProtocol::init()
{
	_fin = 1;
	_opcode = 1;
	_mask = 0;
	memset(_masking_key, 0, sizeof(_masking_key));
	_payload_length = 0;
	memset(_payload_data, 0, sizeof(_payload_data));
}

uint8_t WebSocketProtocol::opcode() const
{
	return _opcode;
}
